Qt on Android的数据库使用(read-only错误)

最近在用Qt写Android App,需要用到数据库,就用了sqlite数据库,创建了几张表,写了些初始数据,准备使用。

遇到第1个问题,如何将数据库db文件放到手机里,这个网上有,放在assets文件夹下就自动打包带入,很快解决了。

第2个问题,带入之后如何连接数据库,这个网上也有,说复制到到目录/data/data/your package/database下就行(我直接使用了Qfile::copy )。

第3个问题,尝试修改数据库数据库时,发现报数据库属性只读(Read-only)不能修改错误,也去网上搜,很多都说是App权限问题,我一查的确没加,就加上了USB存储的修改权限,然而,错误没消除,经过2个晚上的尝试,确认了是db文件的只读属性引起的。解决方法是在将文件复制到/data/data/your package/database时,不使用Qfile::copy方法,而是引入两个文件流(QDataStream),读取源文件数据后直接写入目标文件,直至文件全部读完写完。整个过程可以看做是自己实现了copy方法,与直接使用copy方法的不同是,目标文件是由App创建的,App自动拥有对该目标文件的全部权限,如果使用Qfile::copy方法,因为放在assets带进来的文件必定有只读属性,复制过来的目标文件也必是只读属性。另外,因为/data/data是隐藏目录(非root不可见),Android似乎有个机制是在该目录下对于非自身创建的db文件只能读取,因为采用文件流复制可以完美使用数据库。

代码参考如下:

QString target = "/data/data/aaa.bb.ccc/databases/qb.db";
QFile file(target);
if(false == file.exists()) {
	//不能直接复制文件,直接复制会成为只读文件,因此自己使用文件流进行复制
	QFile source("assets:/qb.db");
	if(false == file.openWriteOnly) {
		QMessageBox::warning(NULL, QString("Init_CopyDatabase"), QString("Failed to open database at destination."));
		return;
	}

	if(false == source.openReadOnly) {
		QMessageBox::warning(NULL, QString("Init_CopyDatabase"), QString("Failed to open database at source."));
		return;
	}

	QDataStream read(&source);
	QDataStream write(&file);
	read.setVersionQt_5_7;
	write.setVersionQt_5_7;
	char acBuffer[1024] = {0};
	int iReadLen = 0;
	while(false == read.atEnd()) {
		iReadLen = read.readRawData(acBuffer, 1024);
		write.writeRawData(acBuffer, iReadLen);
	}
	source.close();
	file.close();
}